/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mttestclient;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

/**
 *
 * @author 0914064 - Ross Aitken
 */
public class TestModel {
    // initialises the variables
    private ArrayList<ClientModel> clients = new ArrayList<ClientModel>();
    private ArrayList<String> options = new ArrayList<String>();
    private int connectionPort = 23;
    private String connectionStartAddress = "127.0.0.1";
    private boolean synchroniseClients = false;
    private Random random = new Random();
    private ArrayList<Timer> timers = new ArrayList<Timer>();
    private ReentrantLock clientLock = new ReentrantLock();
    private int offsetType = Calendar.SECOND;
    private int offsetQuantity = 15;
    private boolean autoincrementIP = true;
    private boolean continuousTesting = false;
    private int maxThreads = 25;
    private ExecutorService threadPoolExecutor = null;

    public TestModel(String inFileName) {
        if (inFileName != null) {
            try {
                BufferedReader in = new BufferedReader(
                        new FileReader(inFileName));
                String voteOption;
                String syncOption, ipOption;
                this.connectionStartAddress = in.readLine();
                this.connectionPort = Integer.parseInt(in.readLine());
                syncOption = in.readLine();
                if (syncOption.toLowerCase().contains("cascade")) {
                    // Creates the option to set up threads cascading
                    synchroniseClients = false; 
                }
                else if (syncOption.toLowerCase().contains("sync")) {
                    // Creates the option to set up threads in sync
                    synchroniseClients = true; 
                }
                ipOption = in.readLine();
                if (ipOption.toLowerCase().contains("autoinc")) {
                    // creates the option to auto-increment the IP address
                    autoincrementIP = true; 
                }
                else if (ipOption.toLowerCase().contains("static")) {
                    // creates the option to create a static IP address
                    autoincrementIP = false; 
                }
                offsetQuantity = Integer.parseInt(in.readLine());
                while ((voteOption = in.readLine()) != null) {
                    // reads in the possible votes from the clients as strings
                    options.add(voteOption); 
                }
                in.close();
            } catch (IOException ex) {
            }
        }
        
        threadPoolExecutor = Executors.newFixedThreadPool(maxThreads);
    }

    public void startTest(int clientCount) { // Starts the testing
        Calendar calendar;
        Date wakeup;
        ClientModel newClient;
        Timer timer;
        String randomVote;
        boolean test = true;

        while (test) {
            calendar = Calendar.getInstance();
            // add calendar to sync the threads
            calendar.add(offsetType, offsetQuantity); 
            wakeup = calendar.getTime();
            for (int i = 0; i < clientCount; i++) {
                // creates a random vote for testing purposes
                randomVote = options.get(random.nextInt(options.size())); 

                clients.add(newClient = new ClientModel(connectionStartAddress, 
                        connectionPort, randomVote)); // creates the client

                // randomly decides if the vote is valid
                if (random.nextBoolean())
                    newClient.ConfirmVote(options.get(
                            random.nextInt(options.size()))); 
                else
                    // casts the same vote
                    newClient.ConfirmVote(newClient.getClientVote()); 

                // links each thread to a timer and launches them simultaneously
                if (synchroniseClients) { 
                    timers.add(timer = new Timer());
                    timer.schedule(new SyncServerRequest(newClient, clientLock), 
                            wakeup);
                }
                else {
                    threadPoolExecutor.execute(
                            new ServerRequest(newClient, clientLock));
                }

                if (autoincrementIP) { // increments the IP address
                    incrementIP();
                }
            }
            if (!continuousTesting) {
                test = false;
            }
        }
    }

    // method to read clients from the list and creates 
    // a string with the information
    String getClientInfo() { 
        String clientOutput = "";
        int index = 0;
        clientLock.lock(); // Locks the client array
        try {
            for (ClientModel client : clients) {
                clientOutput += "Client " + index + " status: \n";
                clientOutput += "Vote: " + client.getClientVote() + "\n";
                clientOutput += "Valid: " + client.isVoteValid() + "\n";
                clientOutput += "Feedback: " + client.getServerFeedback() +"\n";
                
                index++;
            }
        } finally {
            clientLock.unlock(); 
        }
        return clientOutput;
    }

    private void incrementIP() { // Method for incrementing the IP address
        String[] ipParts = connectionStartAddress.split("\\.");
        int[] ipByteParts = new int[4];
        boolean incremented = false; 
        
        // increments the last set of digits until it 
        // reaches 255 then increments the next set
        for (int i = 3; i >= 0; i--) { 
            ipByteParts[i] = Integer.parseInt(ipParts[i]);
            if (ipByteParts[i] < 255 && !incremented) {
                ipByteParts[i]++;
                for (int j = i + 1; j < 4; j++) 
                    ipByteParts[j] = 0;
                if (i != 3) // if the last part of the IP is 0 then set it to 1
                    ipByteParts[3] = 1;
                incremented = true;
            }
        }
        
        connectionStartAddress = ipByteParts[0] + "." + ipByteParts[1] + "." + 
                ipByteParts[2] + "." + ipByteParts[3];
    }

    public int getConnectionPort() {
        return connectionPort;
    }

    public void setConnectionPort(int connectionPort) {
        this.connectionPort = connectionPort;
    }

    public String getConnectionStartAddress() {
        return connectionStartAddress;
    }

    public void setConnectionStartAddress(String connectionStartAddress) {
        this.connectionStartAddress = connectionStartAddress;
    }

    public boolean isContinuousTesting() {
        return continuousTesting;
    }

    public void setContinuousTesting(boolean continuousTesting) {
        this.continuousTesting = continuousTesting;
    }

    public int getOffsetQuantity() {
        return offsetQuantity;
    }

    public void setOffsetQuantity(int offsetQuantity) {
        this.offsetQuantity = offsetQuantity;
    }

    public ArrayList<String> getOptions() {
        return options;
    }

    public void setOptions(ArrayList<String> options) {
        this.options = options;
    }

    public boolean isSynchroniseClients() {
        return synchroniseClients;
    }

    public void setSynchroniseClients(boolean synchroniseClients) {
        this.synchroniseClients = synchroniseClients;
    }

}